﻿#region Using
#if NUNIT
using System;
using System.Data;
using NUnit.Framework;
using TestClass = NUnit.Framework.TestFixtureAttribute;
using TestMethod = NUnit.Framework.TestAttribute;
using TestInitialize = NUnit.Framework.SetUpAttribute;
using TestCleanup = NUnit.Framework.TearDownAttribute;
using TestContext = System.Object;
#else
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Category = Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute;
#endif

using NMock2;
using NMock2.Matchers;

#endregion

namespace NMock3.Tests
{
	[TestClass]
	public class MethodTests
	{
		private MockFactory _factory;
		private Mock<ITest> _mock;

		public MethodTests()
		{
			_factory = new MockFactory();
		}

		[TestInitialize]
		public void TestInit()
		{
			_mock = _factory.CreateMock<ITest>();
		}

		[TestCleanup]
		public void TestClean()
		{
			_factory.VerifyAllExpectationsHaveBeenMet();
		}

		[Test]
		public void VoidMethods()
		{
			_mock.Expects.One.Method(m => m.ReturnsVoidNoArguments()).Comment("");
			_mock.Expects.One.Method(m => m.ReturnsVoidOneArgument("10")).Comment("");

			_mock.MockObject.ReturnsVoidNoArguments();
			_mock.MockObject.ReturnsVoidOneArgument("10");
		}

		[Test]
		[Ignore] //TODO:understand matchers better
		public void VoidMethodsWithMatchers()
		{
			//_mock.Expects.One.Method(m => m.ReturnsVoidNoArguments()).Matching().Comment("");
			_mock.Expects.One.Method(m => m.ReturnsVoidOneArgument("20")).Matching(new TypeMatcher(typeof(string))).Comment("");

			//_mock.MockObject.ReturnsVoidNoArguments();
			_mock.MockObject.ReturnsVoidOneArgument("20");
		}

		[Test]
		public void VoidMethodsWithWith()
		{
			//_mock.Expects.One.Method(m => m.ReturnsVoidNoArguments()).With().Comment("");
			_mock.Expects.One.Method(m => m.ReturnsVoidOneArgument("30")).With("30").Comment("");

			//_mock.MockObject.ReturnsVoidNoArguments();
			_mock.MockObject.ReturnsVoidOneArgument("30");
		}

		[Test]
		public void VoidMethodsWithWithAny()
		{
			_mock.Expects.One.Method(m => m.ReturnsVoidNoArguments()).WithAnyArguments().Comment("");
			_mock.Expects.One.Method(m => m.ReturnsVoidOneArgument("40")).WithAnyArguments().Comment("");

			_mock.MockObject.ReturnsVoidNoArguments();
			_mock.MockObject.ReturnsVoidOneArgument("40");
		}

		[Test]
		public void VoidMethodsWithWithNo()
		{
			_mock.Expects.One.Method(m => m.ReturnsVoidNoArguments()).WithNoArguments().Comment("");
			//_mock.Expects.One.Method(m => m.ReturnsVoidOneArgument("50")).WithNoArguments().Comment("");

			_mock.MockObject.ReturnsVoidNoArguments();
			//_mock.MockObject.ReturnsVoidOneArgument("50");
		}

		[Test]
		[ExpectedException(typeof(Exception))]
		public void VoidMethodsWill1()
		{
			_mock.Expects.One.Method(m => m.ReturnsVoidNoArguments()).Will(Throw.Exception(new Exception()));

			_mock.MockObject.ReturnsVoidNoArguments();
		}

		[Test]
		[ExpectedException(typeof(Exception))]
		public void VoidMethodsWill2()
		{
			_mock.Expects.One.Method(m => m.ReturnsVoidOneArgument("55")).Will(Throw.Exception(new Exception()));

			_mock.MockObject.ReturnsVoidOneArgument("55");
		}

		[Test]
		public void VoidMethodWiths()
		{
			_mock.Expects.One.MethodWith(m => m.ReturnsVoidNoArguments()).Comment("");
			_mock.Expects.One.MethodWith(m => m.ReturnsVoidOneArgument("60")).Comment("");

			_mock.MockObject.ReturnsVoidNoArguments();
			_mock.MockObject.ReturnsVoidOneArgument("60");
		}

		[Test]
		public void StringMethodsWillReturn()
		{
			_mock.Expects.One.Method(m => m.ReturnsStringNoArguments()).WillReturn("1000").Comment("");
			_mock.Expects.One.Method(m => m.ReturnsStringOneArgument("70")).WillReturn("1001").Comment("");

			Assert.AreEqual("1000", _mock.MockObject.ReturnsStringNoArguments());
			Assert.AreEqual("1001", _mock.MockObject.ReturnsStringOneArgument("70"));
		}

		[Test]
		public void StringMethodsMatchingWillReturn()
		{
			//_mock.Expects.One.Method(m => m.ReturnsStringNoArguments()).Matching().WillReturn("1002").Comment("");
			_mock.Expects.One.Method(m => m.ReturnsStringOneArgument("80")).Matching(new TypeMatcher(typeof(string))).WillReturn("1003").Comment("");

			//Assert.AreEqual("1002", _mock.MockObject.ReturnsStringNoArguments());
			Assert.AreEqual("1003", _mock.MockObject.ReturnsStringOneArgument("80"));
		}

		[Test]
		public void StringMethodsWithAnyWillReturn()
		{
			_mock.Expects.One.Method(m => m.ReturnsStringNoArguments()).WithAnyArguments().WillReturn("1004").Comment("");
			_mock.Expects.One.Method(m => m.ReturnsStringOneArgument("90")).WithAnyArguments().WillReturn("1005").Comment("");

			Assert.AreEqual("1004", _mock.MockObject.ReturnsStringNoArguments());
			Assert.AreEqual("1005", _mock.MockObject.ReturnsStringOneArgument("90"));
		}

		[Test]
		public void StringMethodsWithWillReturn()
		{
			_mock.Expects.One.Method(m => m.ReturnsStringNoArguments()).With().WillReturn("1006").Comment("");
			_mock.Expects.One.Method(m => m.ReturnsStringOneArgument("100")).With("100").WillReturn("1007").Comment("");

			Assert.AreEqual("1006", _mock.MockObject.ReturnsStringNoArguments());
			Assert.AreEqual("1007", _mock.MockObject.ReturnsStringOneArgument("100"));
		}

		[Test]
		public void StringMethodsWithNoWillReturn()
		{
			_mock.Expects.One.Method(m => m.ReturnsStringNoArguments()).WithNoArguments().WillReturn("1008").Comment("");
			_mock.Expects.One.Method(m => m.ReturnsStringOneArgument("110")).WithNoArguments().WillReturn("1009").Comment("");

			Assert.AreEqual("1008", _mock.MockObject.ReturnsStringNoArguments());
			Assert.AreEqual("1009", _mock.MockObject.ReturnsStringOneArgument("110"));
		}

		[Test]
		public void StringMethodWithsWill()
		{
			_mock.Expects.One.MethodWith(m => m.ReturnsStringNoArguments()).Will(Return.Value("1010")).Comment("");
			_mock.Expects.One.MethodWith(m => m.ReturnsStringOneArgument("120")).Will(Return.Value("1011")).Comment("");

			Assert.AreEqual("1010", _mock.MockObject.ReturnsStringNoArguments());
			Assert.AreEqual("1011", _mock.MockObject.ReturnsStringOneArgument("120"));
		}

		[Test]
		public void StringMethodWithsWillReturn()
		{
			_mock.Expects.One.MethodWith(m => m.ReturnsStringNoArguments()).WillReturn("1012").Comment("");
			_mock.Expects.One.MethodWith(m => m.ReturnsStringOneArgument("130")).WillReturn("1013").Comment("");

			Assert.AreEqual("1012", _mock.MockObject.ReturnsStringNoArguments());
			Assert.AreEqual("1013", _mock.MockObject.ReturnsStringOneArgument("130"));
		}

		[Test]
		public void StringMethodWiths()
		{
			Outer outer = new Outer();
			outer.Inner = new Inner();
			outer.Inner.Prop = string.Empty;

			_mock.Expects.One.MethodWith(m => m.ReturnsStringNoArguments(), "1014").Comment("");
			_mock.Expects.One.MethodWith(m => m.ReturnsStringOneArgument("140"), "1015").Comment("");
			_mock.Expects.One.MethodWith(m => m.ReturnsVoidOneArgument(string.Empty));
			_mock.Expects.One.MethodWith(m => m.ReturnsVoidOneArgument(outer.Inner.Prop));
			outer.Inner.Prop = "testing";
			_mock.Expects.One.MethodWith(m => m.ReturnsVoidOneArgument(outer.Inner.Prop));
			_mock.Expects.One.MethodWith(m => m.ReturnsVoidOneArgument(Outer.CONST));
			_mock.Expects.One.MethodWith(m => m.ReturnsVoidOneArgument(outer.Inner.CONST));
			_mock.Expects.One.MethodWith(m => m.ReturnsVoidOneArgument(outer.Inner.GetString()));
			_mock.Expects.One.MethodWith(m => m.ReturnsVoidOneArgument(new Outer { Inner = new Inner { Prop = "dynamic" } }.Inner.Prop));
			//_mock.Expects.One.MethodWith(m => m.ReturnsVoidOneArgument(() => { return string.Empty; }));

			Assert.AreEqual("1014", _mock.MockObject.ReturnsStringNoArguments());
			Assert.AreEqual("1015", _mock.MockObject.ReturnsStringOneArgument("140"));
			_mock.MockObject.ReturnsVoidOneArgument(string.Empty);
			_mock.MockObject.ReturnsVoidOneArgument(string.Empty);
			_mock.MockObject.ReturnsVoidOneArgument("testing");
			_mock.MockObject.ReturnsVoidOneArgument("Constant");
			_mock.MockObject.ReturnsVoidOneArgument("Constant1");
			_mock.MockObject.ReturnsVoidOneArgument("Method");
			_mock.MockObject.ReturnsVoidOneArgument("dynamic");
		}

		public interface ITest
		{
			void ReturnsVoidNoArguments();
			void ReturnsVoidOneArgument(string arg1);
			string ReturnsStringNoArguments();
			string ReturnsStringOneArgument(string arg1);

			object Complex1(int i, DateTime d, string s, bool b, Guid g, long? l, object o, DataSet ds);
			object Complex2(string s, ref DataSet ds);
			object Complex3(string s, out DataSet ds);
			object Complex4(int i, string s, params Exception[] errors);
		}

		public class Outer
		{
			public const string CONST = "Constant";

			public Inner Inner { get; set; }
		}

		public class Inner
		{
			public string CONST = "Constant1";

			public string Prop { get; set; }

			public string GetString()
			{
				return "Method";
			}
		}


	}
}
